/**
 * 遍历树形结构
 * @param data 树形结构数据
 * @param callback 回调 参数（当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {boolean}
 */
import {isArray, isString} from "./TypeUtil";

// @ts-ignore
const treeEach: Function = (data: any, callback: Function, field: string = 'children', parent: any, level: number = 0): boolean => {
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i];
        if (callback.call(node, node, parent, i, level, data) === false) {
            return false;
        }
        if (node[field] && node[field].length) {
            if (treeEach(node[field], callback, field, node, level + 1) === false) {
                return false;
            }
        }
    }
};

/**
 * 异步遍历树形结构 回调允许使用异步
 * @param data 树形结构数据
 * @param callback 回调 参数（当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {boolean}
 */
// @ts-ignore
const treeEachAsync: Function = async (data: any, callback: Function, field: string = 'children', parent: any, level: number = 0): boolean => {
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i];
        if ((await callback.call(node, node, parent, i, level, data)) === false) {
            return false;
        }
        if (node[field] && node[field].length) {
            if ((await treeEachAsync(node[field], callback, field, node, level + 1)) === false) {
                return false;
            }
        }
    }
};

/**
 * 遍历树结构分组
 * @param data 树结构数据
 * @param callback 回调函数, 返回组名
 * @param field 子数据在对象中的名称 默认 children
 * @returns {{}}
 */
// @ts-ignore
const treeGroup: Function = (data: any, callback: Function, field: string = 'children'): object => {
    let result: any = {};
    treeEach(data, (...args: any[]) => {
        let groupName: string = callback(...args),
            groupArr: any[] = result[groupName];
        if (!groupArr) {
            groupArr = result[groupName] = [];
        }
        groupArr.push(args[0]);
    }, field);
    return result;
};

/**
 * 使用唯一字段生成map
 * @param data
 * @param callback 可以是唯一字段名 或 方法
 * @returns {{}}
 */
// @ts-ignore
const treeCreateMap: Function = (data: any, callback: any): object => {
    let result: any = {},
        isS: boolean = isString(callback);
    treeEach(data, (...args: any[]) => {
        let item: any = args[0];
        if (isS) {
            result[item[callback]] = item;
        } else {
            result[item[callback(...args)]] = item;
        }
    });
    return result;
};

/**
 * 遍历数组分组
 * @param data 数组
 * @param callback 回调函数或熟悉名, 返回组名
 * @returns {{}}
 */
// @ts-ignore
const arrayGroup: Function = (data: any, callback: any): object => {
    let result: any = {},
        isS: boolean = isString(callback);
    data.forEach((...args: any[]) => {
        let item: any = args[0],
            groupName: string = isS ? item[callback] : callback(...args),
            groupArr: any[] = result[groupName];
        if (!groupArr) {
            groupArr = result[groupName] = [];
        }
        groupArr.push(args[0]);
    });
    return result;
};

/**
 * 使用唯一字段生成map
 * @param data
 * @param callback 可以是唯一字段名 或 方法
 * @returns {{}}
 */
// @ts-ignore
const arrayCreateMap: Function = (data: any, callback: any): object => {
    let result: any = {},
        isS: boolean = isString(callback);
    data.forEach((...args: any[]) => {
        let item: any = args[0];
        if (isS) {
            result[item[callback]] = item;
        } else {
            result[item[callback(...args)]] = item;
        }
    });
    return result;
};

/**
 * 遍历树形结构，返回callback结果的组合
 * @param data 树形结构数据
 * @param callback 回调 参数（子节点返回集合, 当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {Array} 返回组装好的数组
 */
// @ts-ignore
const treeMap: Function = (data: any, callback: Function, field: string = 'children', parent: any, level: number = 0): any[] => {
    let result: any[] = [];
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i],
            childResults: any[] = [];
        // 子数据不是数组则转化为数组
        if (node[field] && !isArray(node[field])) {
            node[field] = [node[field]];
        }
        // 先执行子节点，获取子节点处理返回的集合
        if (node[field] && node[field].length) {
            childResults = treeMap(node[field], callback, field, node, level + 1);
        }
        let newNode = callback.call(node, childResults, node, parent, i, level, data);
        // 将子节点返回的集合组装
        if (newNode) {
            result.push(newNode);
        }
    }
    return result;
};
/**
 * 异步遍历树形结构，返回callback结果的组合
 * @param data 树形结构数据
 * @param callback 回调 参数（子节点返回集合, 当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {Array} 返回组装好的数组
 */
// @ts-ignore
const treeMapAsync: Function = async (data: any, callback: Function, field: string = 'children', parent: any, level: number = 0): any[] => {
    let result: any[] = [];
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i],
            childResults: any[] = [];
        // 子数据不是数组则转化为数组
        if (node[field] && !isArray(node[field])) {
            node[field] = [node[field]];
        }
        // 先执行子节点，获取子节点处理返回的集合
        if (node[field] && node[field].length) {
            childResults = await treeMapAsync(node[field], callback, field, node, level + 1);
        }
        // 将子节点返回的集合组装
        let res = await callback.call(node, childResults, node, parent, i, level, data);
        if (res) {
            result.push(res);
        }
    }
    return result;
};

/**
 * 获取树形结构中所有符合条件的节点   列表结构
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @param result 符合条件的数据 递归用 直接调用不用传
 * @returns {Array}
 */
// @ts-ignore
const treeFilter: Function = (data: any, callback: Function, field: string = 'children', parent: any, level: number = 0, result: any[] = []): any[] => {
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i];
        if (callback.call(node, node, parent, i, level, data) === true) {
            result.push(node)
        }
        if (node[field] && node[field].length) {
            treeFilter(node[field], callback, field, node, level + 1, result)
        }
    }
    return result;
};
/**
 * 获取树形结构中所有符合条件的节点    保留树结构
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @param result 符合条件的数据 递归用 直接调用不用传
 * @returns {Array}
 */
// @ts-ignore
const treeFilterMap: Function = (data: any, callback: Function, field: string = 'children', parent: any = null, level: number = 0): any[] => {
    let result = [];
    for (let i = 0; i < data.length; i++) {
        let node = data[i], childResults = [];
        if (node[field] && node[field].length) {
            childResults = treeFilterMap(node[field], callback, field, node, level + 1);
        }
        if (callback.call(node, node, parent, i, level, data) === true) {
            result.push({
                ...node,
                [field]: node[field] !== null ? childResults : null
            })
        } else if (childResults.length) {
            result.push({
                ...node,
                [field]: node[field] !== null ? childResults : null
            })
        }
    }
    return result;
};

/**
 * 获取树形结构中第一个符合条件的节点
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {Array}
 */
// @ts-ignore
const treeFind: Function = (data: any = [], callback: Function, field: string = 'children', parent: any, level: number = 0): any[] | null => {
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i];
        if (callback.call(node, node, parent, i, level, data) === true) {
            return node;
        }
        if (node[field] && node[field].length) {
            let result: any[] = treeFind(node[field], callback, field, node, level + 1)
            if (result) {
                return result;
            }
        }
    }
    return null;
};

/**
 * 获取一个节点的索引路径 如：[0,2,1,0] 第一个数字代表第一层的索引，第二个第二层... 没找到则返回null
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @param result path路径 递归用 直接调用不用传
 * @returns {Array}
 */
// @ts-ignore
const treePath: Function = (data: any, callback: Function, field: string = "children", parent: any, level: number = 0, result: any[]): any[] | null => {
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i];
        result.push(i);
        if (callback.call(node, node, parent, i, level, data) === true) {
            return result;
        }
        if (node[field] && node[field].length) {
            if (treePath(node[field], callback, field, node, level + 1, result)) {
                return result
            }
        }
        result.pop();
    }
    return null;
};

// @ts-ignore
const treePathNode: Function = (data: any, callback: Function, field: string = "children", parent: any, level: number = 0, result: any[] = []): any[] | null => {
    for (let i = 0; i < data.length; i++) {
        let node: any = data[i];
        result.push(node);
        if (callback.call(node, node, parent, i, level, data) === true) {
            return result;
        }
        if (node[field] && node[field].length) {
            if (treePathNode(node[field], callback, field, node, level + 1, result)) {
                return result
            }
        }
        result.pop();
    }
    return null;
};


export {
    treeEach,
    treeEachAsync,
    treeGroup,
    treeCreateMap,
    arrayGroup,
    arrayCreateMap,
    treeMap,
    treeMapAsync,
    treeFilter,
    treeFilterMap,
    treeFind,
    treePath,
    treePathNode
};